package io.github.xxyopen.novel.recommend;

import io.github.xxyopen.novel.dao.entity.StuBehavior;
import io.github.xxyopen.novel.dao.entity.StuTeacher;
import io.github.xxyopen.novel.dao.mapper.StuBehaviorMapper;
import io.github.xxyopen.novel.dao.mapper.StuTeacherMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Slf4j
public class RecommendByStuTeacherService {

    @Autowired
    private StuTeacherMapper stuTeacherMapper;

    public List<Long> recommend(Integer userId){
        List<Long> result = null;
        try {
            List<StuTeacher> stuTeachers = stuTeacherMapper.selectStuTeacherForRecommend();
            DataModel stuBeahaviorDataModel = this.createStuBeahaviorDataModel(stuTeachers);
            // 这里的itemId是teacher_id
            List<Long> itemIdsAttainByUser = this.recommendBasedOnUserByStuTeacher(userId, stuBeahaviorDataModel);
            List<Long> itemIdsAttainByItem = this.recommendBasedOnItemByStuTeacher(userId, stuBeahaviorDataModel);
            // 使用Java 8的Stream去重两个List
            result = Stream.concat(
                            Optional.ofNullable(itemIdsAttainByUser).orElse(Collections.emptyList()).stream(),
                            Optional.ofNullable(itemIdsAttainByItem).orElse(Collections.emptyList()).stream())
                    .distinct()
                    .collect(Collectors.toList());
        } catch (TasteException e) {
            log.error("推荐算法发生错误...");
        }

        return result;
    }

    public List<Long> recommendBasedOnUserByStuTeacher(Integer userId, DataModel dataModel) throws TasteException {
        //获取用户相似程度
        UserSimilarity similarity = new UncenteredCosineSimilarity(dataModel);
        //获取用户邻居
        UserNeighborhood userNeighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);
        //构建推荐器
        Recommender recommender = new GenericUserBasedRecommender(dataModel, userNeighborhood, similarity);
        //推荐2个
        List<RecommendedItem> recommendedItems = recommender.recommend(userId, 5);
        List<Long> itemIds = recommendedItems.stream().map(RecommendedItem::getItemID).collect(Collectors.toList());
        return itemIds;
    }
    public List<Long> recommendBasedOnItemByStuTeacher(Integer userId, DataModel dataModel) throws TasteException {
        //获取用户相似程度
        ItemSimilarity similarity = new UncenteredCosineSimilarity(dataModel);
        //构建推荐器
        Recommender recommender = new GenericItemBasedRecommender(dataModel, similarity);
        //推荐2个
        List<RecommendedItem> recommendedItems = recommender.recommend(userId, 5);
        List<Long> itemIds = recommendedItems.stream().map(RecommendedItem::getItemID).collect(Collectors.toList());
        return itemIds;
    }
    private DataModel createStuBeahaviorDataModel(List<StuTeacher> stuTeachers) {
        FastByIDMap<PreferenceArray> fastByIdMap = new FastByIDMap<>();
        Map<Long, List<StuTeacher>> map = stuTeachers.stream().collect(Collectors.groupingBy(StuTeacher::getStuId));
        Collection<List<StuTeacher>> list = map.values();
        for(List<StuTeacher> userPreferences : list){
            GenericPreference[] array = new GenericPreference[userPreferences.size()];
            for(int i = 0; i < userPreferences.size(); i++){
                StuTeacher stuTeacher = userPreferences.get(i);
                GenericPreference item = new GenericPreference(stuTeacher.getStuId(), stuTeacher.getTeacherId(), stuTeacher.getValue());
                array[i] = item;
            }
            fastByIdMap.put(array[0].getUserID(), new GenericUserPreferenceArray(Arrays.asList(array)));
        }
        return new GenericDataModel(fastByIdMap);
    }
}
